package com.franklin.java.configuration.sql.type;


import com.franklin.java.api.state.BasePersistentState;
import com.franklin.java.entity.sql.MappingType;
import com.franklin.java.entity.sql.TypeMapping;
import com.franklin.java.entity.group.TypeMappingGroup;
import com.franklin.plugins.common.collection.PrettyArrayList;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import com.intellij.util.xmlb.annotations.OptionTag;

import java.util.*;
import java.util.stream.Collectors;


/**
 * 存储类型映射配置，以应用存储
 *
 * @author Franklin
 * @since 2021/2/7 11:22
 */
@State(
    name = "com.franklin.java.configuration.sql.type.TypeMappingState",
    storages = {@Storage(TypeMappingState.PERSISTENT_PATH + "TypeMappingSetting.xml")}
)
public class TypeMappingState extends BasePersistentState<TypeMappingState> {

    @OptionTag(converter = TypeMappingMapConverter.class)
    public Map<String, TypeMappingGroup> groupNameTypeMappingGroupMap = getDefaultValue();

    public static TypeMappingState getInstance() {
        return ServiceManager.getService(TypeMappingState.class);
    }


    public static TypeMappingGroup getCurrentMappingGroup(Project project) {
        TypeMappingState instance = getInstance();
        return instance.groupNameTypeMappingGroupMap.get(SelectGroupState.getSelectGroup(project));
    }

    /**
     * 翻转隐射<br>
     * @author Franklin
     * @since 2021/9/2 16:08
     * @param project:
     * @return java.util.Map<java.lang.String,java.lang.String>
     */
    public static Map<String, String> getReverseMapping(Project project){
        TypeMappingGroup currentMappingGroup = getCurrentMappingGroup(project);
        List<TypeMapping> elementList = currentMappingGroup.getElementList();
        //不适用Stream API防止重复Key
        Map<String,String> result = new HashMap<>();
        elementList.forEach(typeMapping -> result.put(typeMapping.getJavaType(),typeMapping.getColumnType()));
        return result;
    }

    public static MappingType getMappingType(Project project) {
        TypeMappingGroup currentMappingGroup = getCurrentMappingGroup(project);
        List<TypeMapping> typeMappingList = currentMappingGroup.getElementList();
        Map<Boolean, Map<String, String>> hasLengthMap = typeMappingList.stream()
            .collect(Collectors.groupingBy(
                typeMapping -> typeMapping.getColumnType().contains("("),
                Collectors.toMap(TypeMapping::getColumnType, TypeMapping::getJavaType)
            ));

        Map<String, String> hasLength = hasLengthMap.getOrDefault(true, Collections.emptyMap());
        Map<String, String> noLength = hasLengthMap.getOrDefault(false, Collections.emptyMap());

        return new MappingType(noLength, hasLength);
    }

    Map<String, TypeMappingGroup> getDefaultValue() {
        Map<String, TypeMappingGroup> map = new LinkedHashMap<>();

        TypeMappingGroup typeMappingGroup = new TypeMappingGroup();
        List<TypeMapping> typeMappingList = new PrettyArrayList<>();
        typeMappingList.add(new TypeMapping("varchar(\\(\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("char(\\(\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("text", "java.lang.String"));
        typeMappingList.add(new TypeMapping("decimal(\\(\\d+\\))?", "java.lang.Double"));
        typeMappingList.add(new TypeMapping("decimal(\\(\\d+,\\d+\\))?", "java.lang.Double"));
        typeMappingList.add(new TypeMapping("integer", "java.lang.Integer"));
        typeMappingList.add(new TypeMapping("tinyint(\\(\\d+\\))?", "java.lang.Integer"));
        typeMappingList.add(new TypeMapping("int(\\(\\d+\\))?", "java.lang.Integer"));
        typeMappingList.add(new TypeMapping("int4", "java.lang.Integer"));
        typeMappingList.add(new TypeMapping("int8", "java.lang.Long"));
        typeMappingList.add(new TypeMapping("bigint(\\(\\d+\\))?", "java.lang.Long"));
        typeMappingList.add(new TypeMapping("datetime", "java.util.Date"));
        typeMappingList.add(new TypeMapping("timestamp", "java.util.Date"));
        typeMappingList.add(new TypeMapping("boolean", "java.lang.Boolean"));

        typeMappingGroup.setName(SelectGroupState.DEFAULT_GROUP);
        typeMappingGroup.setElementList(typeMappingList);

        map.put(typeMappingGroup.getName(), typeMappingGroup);

        TypeMappingGroup franklinMapping = franklinMapping();
        map.put(franklinMapping.getName(), franklinMapping);

        return map;
    }

    private TypeMappingGroup franklinMapping() {
        TypeMappingGroup typeMappingGroup = new TypeMappingGroup();
        List<TypeMapping> typeMappingList = new PrettyArrayList<>();
        typeMappingList.add(new TypeMapping("varchar(\\(\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("char(\\(\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("text", "java.lang.String"));
        typeMappingList.add(new TypeMapping("decimal(\\(\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("decimal(\\(\\d+,\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("integer", "java.lang.String"));
        typeMappingList.add(new TypeMapping("tinyint(\\(\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("int(\\(\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("int4", "java.lang.String"));
        typeMappingList.add(new TypeMapping("int8", "java.lang.String"));
        typeMappingList.add(new TypeMapping("bigint(\\(\\d+\\))?", "java.lang.String"));
        typeMappingList.add(new TypeMapping("datetime", "java.lang.String"));
        typeMappingList.add(new TypeMapping("timestamp", "java.lang.String"));
        typeMappingList.add(new TypeMapping("boolean", "java.lang.String"));

        typeMappingGroup.setName("franklin");
        typeMappingGroup.setElementList(typeMappingList);

        return typeMappingGroup;
    }
}
